package com.yyfax.pay.depository.service.inner.impl;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.annotation.Resource;

import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import com.yyfax.commons.lang.DateUtil;
import com.yyfax.framework.common.Log;
import com.yyfax.framework.yyfax.CryptUtil;
import com.yyfax.framework.yyfax.DesensitizationUtil;
import com.yyfax.pay.common.constants.FuiouDepositConstant;
import com.yyfax.pay.common.enums.FileServcerStateEnum;
import com.yyfax.pay.common.enums.FuiouBusiTypeEnum;
import com.yyfax.pay.common.enums.FuiouDepositChangeCardTypeEnum;
import com.yyfax.pay.common.enums.FuiouDepositRspCodeEnum;
import com.yyfax.pay.common.enums.FuiouDepositStateEnum;
import com.yyfax.pay.common.enums.PaymentEnum;
import com.yyfax.pay.common.exception.PayExceptionCode;
import com.yyfax.pay.common.model.vo.PayRspCodeVO;
import com.yyfax.pay.common.model.vo.ValidResult;
import com.yyfax.pay.common.util.FuiouIdUtils;
import com.yyfax.pay.config.data.service.BankConfigExtService;
import com.yyfax.pay.config.data.service.BankConfigService;
import com.yyfax.pay.config.model.po.BankConfigExtPO;
import com.yyfax.pay.config.model.po.BankConfigPO;
import com.yyfax.pay.depository.dao.FuiouChangeCardDao;
import com.yyfax.pay.depository.domain.FuiouChangeCard;
import com.yyfax.pay.depository.service.inner.FuiouChangeCardService;
import com.yyfax.pay.depository.vo.FuiouChangeCardVO;
import com.yyfax.pay.order.data.service.UserPaymentService;
import com.yyfax.pay.order.model.po.UserPaymentPO;
import com.yyfax.pay.order.service.PayCommonService;
import com.yyfax.pay.third.facade.file.FileServerService;
import com.yyfax.pay.third.facade.fuioudeposit.FuiouDepositService;
import com.yyfax.pay.third.facade.fuioudeposit.FuiouSignService;
import com.yyfax.pay.third.model.to.file.FileResultDetailVO;
import com.yyfax.pay.third.model.to.file.UploadNotifyReq;
import com.yyfax.pay.third.model.to.fuioudeposit.ChangeUserInfoNotifyReq;
import com.yyfax.pay.third.model.to.fuioudeposit.FuiouDepositConfigExt;
import com.yyfax.pay.third.model.to.yyfax.QueryCardBinContent;
import com.yyfax.pay.vo.depository.UserChangeCardReq;
import com.yyfax.pay.vo.depository.UserChangeCardReq2;

/**
 * @author yejc
 * @date 2017-09-26
 * @since v1.0.0
 */
@Service
public class FuiouChangeCardServiceImpl implements FuiouChangeCardService {
	private Logger logger = LoggerFactory.getLogger(getClass());

	@Resource
	private FuiouIdUtils fuiouIdUtils;
	@Resource
	private FuiouChangeCardDao changeCardDao;
	@Resource
	private FuiouSignService fuiouSignService;
	@Resource
	private FuiouDepositService fuiouDepositService;
	@Resource
	private BankConfigExtService bankConfigExtService;

	@Resource
	private UserPaymentService userPaymentService;
	@Resource
	private PayCommonService payCommonService;

	@Resource
	private FileServerService fileServerService;

	@Resource
	private BankConfigService bankConfigServcice;

	@Override
	public FuiouChangeCard userChangeCard(UserChangeCardReq req, String merchantCode) {
		FuiouDepositConfigExt extInfo = fuiouSignService.getFuiouDepositConfig(merchantCode);
		FuiouChangeCard order = initChangeCardOrder(req, merchantCode, extInfo);

		// 更新订单
		int i = changeCardDao.updateStateByBusiId(FuiouDepositStateEnum.INI.getValue(), FuiouDepositStateEnum.DEALING.getValue(), order.getBusiId(),
				DateUtil.getCurrentDateTime());
		if (i > 0) {
			// 请求存管系统
			PayRspCodeVO payRspCodeVO = fuiouDepositService.userChangeCard(order);

			// 处理返回结果
			handleChangeCard(payRspCodeVO, order);
		}

		return changeCardDao.selectByPrimaryKey(order.getBusiId());
	}

	@Override
	public void handleChangeCardNotify(ChangeUserInfoNotifyReq req, String merchantCode) {
		final String op = "FuiouChangeCardServiceImpl.handleChangeCardNotify";
		if (!FuiouDepositRspCodeEnum.SUCCESS.getValue().equals(req.getResp_code())) {
			changeCardDao.updateStateAndRspByBusiId(FuiouDepositStateEnum.FAIL.getValue(), req.getMchnt_txn_ssn(), DateUtil.getCurrentDateTime(),
					PayExceptionCode.USER_CHANGE_INFO_RESULT_ERROR.getCode(), PayExceptionCode.USER_CHANGE_INFO_RESULT_ERROR.getDesc());
		} else {
			changeCardDao.updateStateAndRspByBusiId(FuiouDepositStateEnum.SUCCESS.getValue(), req.getMchnt_txn_ssn(), DateUtil.getCurrentDateTime(),
					PayExceptionCode.SUCCESS.getCode(), "通知成功");
			FuiouChangeCard fuiouChangeCard = changeCardDao.selectByPrimaryKey(req.getMchnt_txn_ssn());
			if (fuiouChangeCard == null) {
				logger.error(Log.op(op).msg("换卡订单不存在，无法更新用户支付信息").kv("busiId", req.getMchnt_txn_ssn()).toString());
				return;
			}
			updateUserPaymentBankCard(merchantCode, fuiouChangeCard.getMerchantUserId(), req.getCapAcntNo());
		}
	}

	@Override
	public void updateUserPaymentBankCard(String merchantCode, String merchantUserId, String newBankCardNo) {
		final String op = "FuiouChangeCardServiceImpl.updateUserPaymentBankCard";
		try {
			UserPaymentPO paymentPO = userPaymentService.query(merchantCode, merchantUserId, PaymentEnum.FUIOU_DEPOSIT.getValue());
			if (paymentPO == null) {
				logger.error(Log.op(op).msg("换卡成功后，无法更新用户支付信息，user_payment没有找到用户支付信息").kv("merchantCode", merchantCode).kv("userId", merchantUserId)
						.kv("paymentCode", PaymentEnum.FUIOU_DEPOSIT.getValue()).toString());
				return;
			}
			QueryCardBinContent content = payCommonService.queryCardBin(merchantCode, newBankCardNo);
			if (content == null) {
				logger.error(Log.op(op).msg("查询卡bin返回null").kv("cardNo", newBankCardNo).toString());
				return;
			}
			paymentPO.setBankCode(content.getBankCode());
			paymentPO.setBankName(content.getBankName());
			paymentPO.setCardNo(DesensitizationUtil.convertBankCard(newBankCardNo));
			paymentPO.setCardNoCip(CryptUtil.encrypt(newBankCardNo));
			userPaymentService.modify(paymentPO);
		} catch (Exception e) {
			logger.error(Log.op(op).msg("更新用户支付信息user_payment异常").kv("merchantCode", merchantUserId).kv("merchantUserId", merchantUserId).toString(),
					e);
		}
	}

	/**
	 * 处理换卡请求结果
	 *
	 * @author yejc
	 * @date 2017-09-30
	 * @since v2.0.0
	 * @param resp
	 * @param order
	 */
	private void handleChangeCard(PayRspCodeVO resp, FuiouChangeCard order) {
		final String op = "FuiouChangeCardServiceImpl.handleChangeCard";
		// 超时
		if (PayExceptionCode.WARN_REQ_TIME_OUT.isCode(resp.getCode())) {
			logger.warn(Log.op(op).msg("更换银行卡请求超时").kv("busiId", order.getBusiId()).toString());
			// 超时状态下，更改订单状态为已受理 .edit by wangjw since 2.1.2
			changeCardDao.updateStateAndRspByBusiId(FuiouDepositStateEnum.ACCEPTED.getValue(), order.getBusiId(), DateUtil.getCurrentDateTime(),
					resp.getCode(), resp.getDesc());
			throw PayExceptionCode.WARN_REQ_TIME_OUT.exp();
		}

		if (PayExceptionCode.SUCCESS.isCode(resp.getCode())) {
			// 更新订单处理状态为受理成功
			changeCardDao.updateStateAndRspByBusiId(FuiouDepositStateEnum.ACCEPTED.getValue(), order.getBusiId(), DateUtil.getCurrentDateTime(),
					resp.getCode(), resp.getDesc());
		} else {
			// 更新订单处理状态为失败
			changeCardDao.updateStateAndRspByBusiId(FuiouDepositStateEnum.FAIL.getValue(), order.getBusiId(), DateUtil.getCurrentDateTime(),
					resp.getCode(), resp.getDesc());
		}
	}

	/**
	 * 初始化订单或根据商户号和订单号查询
	 *
	 * @author yejc
	 * @date 2017-09-30
	 * @since v2.0.0
	 * @param req
	 * @param merchantCode
	 * @param extInfo
	 * @return
	 */
	private FuiouChangeCard initChangeCardOrder(UserChangeCardReq req, String merchantCode, FuiouDepositConfigExt extInfo) {
		final String op = "FuiouChangeCardServiceImpl.initChangeCardOrder";
		FuiouChangeCard order = new FuiouChangeCard();
		order.setType(FuiouDepositChangeCardTypeEnum.NEW_AND_OLD.getValue());
		order.setOrderTime(req.getOrderTime());
		order.setOrderId(req.getOrderId());
		order.setMerchantCode(merchantCode);
		order.setState(FuiouDepositStateEnum.INI.getValue());
		order.setTerminal(req.getTerminal());
		order.setMerchantUserId(req.getUserId());
		String now = DateUtil.getCurrentDateTime();
		order.setCreateTime(now);
		order.setUpdateTime(now);
		order.setBusiId(fuiouIdUtils.generateBusiId(FuiouBusiTypeEnum.USER_CHANGE_CARD, merchantCode));
		order.setCityCode(FuiouDepositConstant.CITY_CODE_BEIJING); // 默认为北京
		try {
			BankConfigExtPO bankConfigExtPO = bankConfigExtService.queryByThirdBankCode(req.getBankCode(), PaymentEnum.FUIOU_DEPOSIT.getValue());
			String fuiouBankNo = bankConfigExtPO.getBankNoExt();
			order.setBankCode(fuiouBankNo);
		} catch (Exception e1) {
			throw PayExceptionCode.ERROR_ILL_CONFIG_BANK_EXT.exp();
		}
		order.setAccountNo(req.getAccountNo());
		Path imagePath1 = Paths.get(extInfo.getFileStorePrefix(), req.getImageFile1());
		order.setImageFile1(imagePath1.toAbsolutePath().toString());
		if (!StringUtils.isBlank(req.getImageFile2())) {
			order.setImageFile2(Paths.get(extInfo.getFileStorePrefix(), req.getImageFile2()).toAbsolutePath().toString());
		}
		try {
			order.setCardNoCip(CryptUtil.encrypt(req.getBankCardNo()));
		} catch (Exception e) {
			throw PayExceptionCode.SYS_ERROR.exp();
		}
		try {
			changeCardDao.insert(order);
		} catch (DuplicateKeyException e) {
			logger.warn(Log.op(op).msg("订单已存在！").kv("orderId", order.getOrderId()).kv("merchantCode", merchantCode).toString(), e);
			order = changeCardDao.selectByOrderIdMerchantCode(req.getOrderId(), merchantCode);
		}
		return order;
	}

	@Override
	public ValidResult validateChangeCard(String merchantCode, String bankCode, String image1, String image2) {
		final String op = "FuiouChangeCardServiceImpl.validateChangeCard";
		FuiouDepositConfigExt extInfo = fuiouSignService.getFuiouDepositConfig(merchantCode);

		BankConfigExtPO bankConfigExtPO = null;
		try {
			bankConfigExtPO = bankConfigExtService.queryByThirdBankCode(bankCode, PaymentEnum.FUIOU_DEPOSIT.getValue());
			if (bankConfigExtPO == null) {
				throw PayExceptionCode.ERROR_ILL_CONFIG_BANK_EXT.exp();
			}
		} catch (Exception e1) {
			throw PayExceptionCode.ERROR_ILL_CONFIG_BANK_EXT.exp();
		}

		String fuiouBankNo = bankConfigExtPO.getBankNoExt();

		if (StringUtils.isBlank(fuiouBankNo) || fuiouBankNo.length() != 4) {
			logger.error(Log.op(op).msg("查询富友银行代码失败").kv("bankCode", bankCode).toString());
			return ValidResult.newInstance(false, "请求参数错误，不支持的银行代码");
		}

		Path imagePath1 = Paths.get(extInfo.getFileStorePrefix(), image1);
		if (!Files.exists(imagePath1)) {
			return ValidResult.newInstance(false, String.format("图片1不存在，imagePath=[%s]", imagePath1.toAbsolutePath().toString()));
		}

		if (StringUtils.isNotBlank(image2)) {
			Path imagePath2 = Paths.get(extInfo.getFileStorePrefix(), image2);
			if (!Files.exists(imagePath2)) {
				return ValidResult.newInstance(false, String.format("图片2不存在，imagePath=[%s]", imagePath2.toAbsolutePath().toString()));
			}
		}

		return ValidResult.newInstance(true, null);
	}

	@Override
	public ValidResult validChangeCardNew(String merchantCode, String orderId, String bankCode, String... imageFiles) {
		final String op = "FuiouChangeCardServiceImpl.validChangeCardNew";
		FuiouDepositConfigExt extInfo = fuiouSignService.getFuiouDepositConfig(merchantCode);

		BankConfigExtPO bankConfigExtPO = null;
		try {
			bankConfigExtPO = bankConfigExtService.queryByThirdBankCode(bankCode, PaymentEnum.FUIOU_DEPOSIT.getValue());
			if (bankConfigExtPO == null) {
				throw PayExceptionCode.ERROR_ILL_CONFIG_BANK_EXT.exp();
			}
		} catch (Exception e1) {
			throw PayExceptionCode.ERROR_ILL_CONFIG_BANK_EXT.exp();
		}

		String fuiouBankNo = bankConfigExtPO.getBankNoExt();

		if (StringUtils.isBlank(fuiouBankNo) || fuiouBankNo.length() != 4) {
			logger.error(Log.op(op).msg("查询富友银行代码失败").kv("bankCode", bankCode).toString());
			return ValidResult.newInstance(false, "请求参数错误，不支持的银行代码");
		}

		for (String imageFile : imageFiles) {
			Path imagePath = Paths.get(extInfo.getFileStorePrefix(), imageFile);
			if (!Files.exists(imagePath)) {
				return ValidResult.newInstance(false, String.format("图片不存在，imagePath=[%s]", imagePath.toAbsolutePath().toString()));
			}
		}

		return ValidResult.newInstance(true, null);
	}

	@Override
	public FuiouChangeCardVO save(String merchantCode, UserChangeCardReq2 params) throws Exception {
		final String op = "FuiouChangeCardServiceImpl.initChangeCardOrder";
		FuiouChangeCard order = new FuiouChangeCard();
		order.setOrderTime(params.getOrderTime());
		order.setOrderId(params.getOrderId());
		order.setAccountNo(params.getAccountNo());
		order.setMerchantCode(merchantCode);
		order.setType(FuiouDepositChangeCardTypeEnum.NEW.getValue());
		order.setState(FuiouDepositStateEnum.INI.getValue());
		order.setTerminal(params.getTerminal());
		order.setMerchantUserId(params.getUserId());
		String now = DateUtil.getCurrentDateTime();
		order.setCreateTime(now);
		order.setUpdateTime(now);
		order.setBusiId(fuiouIdUtils.generateBusiId(FuiouBusiTypeEnum.USER_CHANGE_CARD, merchantCode));
		order.setCityCode(FuiouDepositConstant.CITY_CODE_BEIJING); // 默认为北京

		BankConfigPO bankConfigPO = bankConfigServcice.queryByBankCode(params.getBankCode());
		if (bankConfigPO == null) {
			throw PayExceptionCode.ERROR_ILL_PARAMETER.expMsg("bankCode无法识别");
		}
		// 保存原接口请求的银行编号
		order.setBankCode(params.getBankCode());
		order.setBankName(bankConfigPO.getBankName());
		order.setAccountNo(order.getAccountNo());

		order.setImageFile1(params.getImageFile());

		order.setCardNoCip(CryptUtil.encrypt(params.getBankCardNo()));

		try {
			changeCardDao.insert(order);
		} catch (DuplicateKeyException e) {
			logger.warn(Log.op(op).msg("订单已存在！").kv("orderId", order.getOrderId()).kv("merchantCode", merchantCode).toString(), e);
			order = changeCardDao.selectByOrderIdMerchantCode(order.getOrderId(), merchantCode);
		}

		return FuiouChangeCardVO.newInstantce(order);
	}

	@Override
	public FuiouChangeCard userChangeCard(FuiouChangeCard po) throws Exception {
		if (FuiouDepositChangeCardTypeEnum.NEW.isValue(po.getType())) {
			// 如果是单卡验证更换银行卡
			return processChangeCardForSingleCard(po);
		} else {
			throw new NotImplementedException("未知实现方式");
		}
	}

	/**
	 * 执行单卡更换银行卡
	 * 
	 * @author buyi
	 * @date 2018-03-29 17:36:27
	 * @since v2.1.3
	 * @param po
	 * @return
	 * @throws Exception
	 */
	private FuiouChangeCard processChangeCardForSingleCard(FuiouChangeCard po) throws Exception {
		// step1.更新订单状态为处理中
		int i = changeCardDao.updateStateFromOriStateByBusiId(po.getBusiId(), FuiouDepositStateEnum.INI.getValue(),
				FuiouDepositStateEnum.DEALING.getValue(), PayExceptionCode.PROCESSING.getCode(), PayExceptionCode.PROCESSING.getDesc());
		if (i <= 0) {
			// 更新没有命中
			return changeCardDao.selectByPrimaryKey(po.getBusiId());
		}

		// step2.上传图片到富友FTP，并获取到最新的文件
		// String actImageFilePath = fuiouDepositService.uploadChangeCardImage(po);
		PayRspCodeVO payRspCodeVO = fuiouDepositService.uploadChangeCardImage(po);
		
		if (PayExceptionCode.SUCCESS.isCode(payRspCodeVO.getCode()) || PayExceptionCode.isTimeout(payRspCodeVO.getCode())) {
			// 如果状态是成功,或者超时，都表示处理中
			String actImageFilePath = (String) payRspCodeVO.getObj();

			// step3. 更新新的图片名称到订单act_image_file1
			changeCardDao.updateActImageFile1(po.getBusiId(), actImageFilePath);
			
			po.setState(FuiouDepositStateEnum.DEALING.getValue());
			// 更换银行，默认返回参数成功，表示已经接收到请求
			po.setRespCode(PayExceptionCode.SUCCESS.getCode());
			po.setRespDesc(PayExceptionCode.SUCCESS.getDesc());
			return po;
		}

		// 其他相应状态，则表示失败
		changeCardDao.updateStateFromOriStateByBusiId(po.getBusiId(), FuiouDepositStateEnum.DEALING.getValue(), FuiouDepositStateEnum.FAIL.getValue(),
				payRspCodeVO.getCode(), payRspCodeVO.getDesc());

		po.setState(FuiouDepositStateEnum.FAIL.getValue());
		po.setRespCode(payRspCodeVO.getCode());
		po.setRespDesc(payRspCodeVO.getDesc());

		return po;
	}

	@Override
	public void parseImageUploadResultForFileServer(String merchantCode, UploadNotifyReq req) {
		final String op = "FuiouChangeCardServiceImpl.parseImageUploadResultForFileServer";
		FuiouChangeCard po = changeCardDao.selectByPrimaryKey(req.getOrderId());
		if (po == null) {
			logger.error(Log.op(op).msg("解析更换银行卡上传图片结果，订单不存在").kv("busiId", req.getOrderId()).toString());
			return;
		}

		FuiouChangeCardVO vo = FuiouChangeCardVO.newInstantce(po);
		if (!vo.isDealing()) {
			logger.warn(Log.op(op).msg("解析更换银行卡上传图片结果，订单处理中状态，不在触发任何逻辑").kv("busiId", req.getOrderId()).toString());
			return;
		}

		FuiouDepositStateEnum state = FuiouDepositStateEnum.FAIL;

		FileResultDetailVO fileResultDetailVO = req.getFiles().get(0);
		if (!FileServcerStateEnum.SUCCESS.isValue(fileResultDetailVO.getState())) {
			// 图片上传不成功,更新订单结果为失败
			logger.error(Log.op(op).msg("更换银行卡，上传图片失败").kv("busiId", po.getBusiId()).toString());
			changeCardDao.updateStateFromOriStateByBusiId(po.getBusiId(), state.getValue(), po.getState(), PayExceptionCode.ERROR_PAY_FAIL.getCode(),
					"图片上传失败");
			return;
		}

		// 请求富友存管更换银行卡
		PayRspCodeVO payRspCode = fuiouDepositService.doChangeCardSingle(po);

		int rspCode = payRspCode.getCode();
		String rspMsg = payRspCode.getDesc();

		// 解析富友响应结果
		if (PayExceptionCode.SUCCESS.isCode(payRspCode.getCode()) || PayExceptionCode.WARN_REQ_TIME_OUT.isCode(payRspCode.getCode())
				|| PayExceptionCode.TO_CONFIRM.isCode(payRspCode.getCode())) {
			// 发送请求结果，表示更换银行卡请求已经被受理，具体审核结果时间为T+1
			state = FuiouDepositStateEnum.ACCEPTED;
		}

		// 更新订单结果
		changeCardDao.updateStateFromOriStateByBusiId(po.getBusiId(), po.getState(), state.getValue(), rspCode, rspMsg);
	}

}
